package cn.iocoder.yudao.framework.pay.core.client.impl.huifu;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.TemporalAccessorUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.pay.core.client.dto.huifu.UserBasicdataRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.huifu.UserBusiOpenRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.huifu.UserInfoReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
import com.alibaba.fastjson.JSONObject;
import com.huifu.bspay.sdk.opps.client.BasePayClient;
import com.huifu.bspay.sdk.opps.core.BasePay;
import com.huifu.bspay.sdk.opps.core.request.*;
import com.huifu.bspay.sdk.opps.core.utils.DateTools;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN;
import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;

/**
 * 汇付支付宝下单
 */
@Slf4j
public class HuifuAlipayWapPayClient extends AbstractHuifuPayClient {

    // 在 cn.iocoder.yudao.framework.pay.core.client.impl.PayClientFactoryImpl——put引用
    public HuifuAlipayWapPayClient(Long channelId, HuifuWxPayClientConfig config) {
        super(channelId, PayChannelEnum.WX_LITE.getCode(), config);
    }

    /**
     * 初始化支付的配置
     */
    @Override
    protected void doInit() {
        super.doInit(Boolean.TRUE, BasePay.MODE_PROD);
    }

    /**
     * 统一下单（汇付 JSPay 接口）
     */
    @Override
    protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Throwable {
        // 1. 构建支付请求参数对象（V2TradePaymentJspayRequest 是汇付下单请求模型）
        V2TradePaymentJspayRequest v2TradePaymentJspayRequest = buildJsPayRequest(reqDTO);
        log.info("[doUnifiedOrder][构建请求参数: {}]", v2TradePaymentJspayRequest);
        // 2. 发起请求，调用汇付接口，返回数据为 Map<String, Object>
        Map<String, Object> response = doExecute(v2TradePaymentJspayRequest);
        // 3. 提取 qr_code 字段
        String payInfoStr = (String) response.get("qr_code");
        // 5. 构建统一返回 DTO
        return PayOrderRespDTO.waitingOf(
                PayOrderDisplayModeEnum.APP.getMode(), // 展示模式：APP 模式
                payInfoStr,                            // 前端需要使用的 pay 参数配置
                reqDTO.getOutTradeNo(),                // 外部交易单号
                response                               // 原始响应内容
        );
    }

    private V2TradePaymentJspayRequest buildJsPayRequest(PayOrderUnifiedReqDTO reqDTO) {
        V2TradePaymentJspayRequest request = new V2TradePaymentJspayRequest();
        // 基础参数
        request.setReqDate(DateTools.getCurrentDateYYYYMMDD());// 请求日期
//        request.setReqSeqId(SequenceTools.getReqSeqId32());// 请求流水号
        request.setReqSeqId(reqDTO.getOutTradeNo());// 请求流水号
        request.setHuifuId(config.getHuifuId());// 商户号
        request.setTradeType("A_NATIVE");//  A_NATIVE(支付宝正扫)
        request.setGoodsDesc(reqDTO.getBody());// 商品描述

        // 金额处理，保留2位小数
        String transAmt = BigDecimal.valueOf(reqDTO.getPrice())
                .divide(BigDecimal.valueOf(100))
                .setScale(2, RoundingMode.HALF_UP)
                .toString();
        request.setTransAmt(transAmt);

        // 扩展字段封装
        Map<String, Object> extendInfoMap = new HashMap<>();
//        extendInfoMap.put("time_expire", DateTools.formatYYYYMMDDHHSS(order.getExpireTime()));// 交易有效期
        extendInfoMap.put("limit_pay_type", "NO_CREDIT");// 禁用信用卡标记
        extendInfoMap.put("delay_acct_flag", "N"); // 是否延迟交易
        extendInfoMap.put("fq_mer_discount_flag", "N"); // 商户贴息标记
        extendInfoMap.put("notify_url", reqDTO.getNotifyUrl());// 异步通知地址
        extendInfoMap.put("remark", reqDTO.getBody()); // 备注

        request.setExtendInfo(extendInfoMap);
        return request;
    }

    /**
     * 汇付回调的支付订单通知
     *
     * @param params  回调参数 Map
     * @param body    请求体（未使用）
     * @param headers 请求头（未使用）
     * @return 支付订单响应 DTO
     */
    @Override
    protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) {
        if (ObjUtil.notEqual(params.get("resp_code"), "00000000")) {
            throw new ServiceException(500, "汇支付网关返回码异常");
        }
        // 1. 获取并解析 resp_data 字段
        String respData = params.get("resp_data");
        if (StrUtil.isBlank(respData)) {
            throw new ServiceException(500, "回调参数缺少 resp_data 字段");
        }

        JSONObject response;
        try {
            response = JSONObject.parseObject(respData);
        } catch (Exception e) {
            throw new ServiceException(500, "resp_data JSON 解析失败：" + respData);
        }

        // 2. 判断交易状态：S 成功，否则为已关闭
        Integer status = "S".equals(response.getString("trans_stat"))
                ? PayOrderStatusRespEnum.SUCCESS.getStatus()
                : PayOrderStatusRespEnum.CLOSED.getStatus();

        // 3. 构建响应 DTO
        return PayOrderRespDTO.of(
                status,
                response.getString("hf_seq_id"),
                response.getString("wx_user_id"),
                parseDate(response.getString("end_time")),
                response.getString("req_seq_id"),
                params
        );
    }

    /**
     * 获得支付订单信息
     * outTradeNo pay_order_extension订单号
     */
    @Override
    protected PayOrderRespDTO doGetOrder(String outTradeNo, LocalDateTime createTime) throws Throwable {
        V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
        // 原机构请求日期
        String reqDate = TemporalAccessorUtil.format(createTime.atZone(ZoneId.systemDefault()), PURE_DATE_PATTERN);
        request.setOrgReqDate(reqDate);
        // 服务订单创建请求流水号
        request.setOrgReqSeqId(outTradeNo);
        request.setHuifuId(config.getHuifuId());  // 商户号
        Map<String, Object> extendInfoMap = new HashMap<>(); // 非必填字段
        request.setExtendInfo(extendInfoMap);
        // 构建 request 请求参数
        Map<String, Object> response = doExecute(request);
        log.info("[汇付支付（支付宝）交易查询][结果参数({})]", response);
        String respCode = toJsonString(response.get("resp_code"));
        if (ObjUtil.equal(respCode, "00000000")) {
            if ("S".equals(response.get("trans_stat"))) {
                return PayOrderRespDTO.successOf(
                        response.get("org_hf_seq_id").toString(),// 交易返回的全局流水号
                        response.get("wx_user_id").toString(),
                        parseDate(response.get("end_time").toString()),
                        response.get("org_req_seq_id").toString(), // 原机构请求流水号
                        response);
            }
        }
        return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
                response.get("org_req_seq_id").toString(), response);
    }

    static LocalDateTime parseDate(String time) {
        return LocalDateTimeUtil.parse(time, PURE_DATETIME_PATTERN);
    }

    // ========================================= 退 款 ==========================================================================
    @Override
    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
        // 1. 构建汇付退款请求对象
        V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
        request.setReqDate(DateTools.getCurrentDateYYYYMMDD()); // 请求日期
//        request.setReqSeqId(SequenceTools.getReqSeqId32()); // 请求流水号
        request.setReqSeqId(reqDTO.getOutRefundNo()); // 请求流水号
        request.setHuifuId(config.getHuifuId()); // 商户号
        request.setOrdAmt(convertPriceToYuan(reqDTO.getRefundPrice()));  // 申请退款金额，单位：元（需要将分转元）
        // 原交易请求日期（支付成功时间转 yyyyMMdd）
        LocalDateTime successTime = reqDTO.getSuccessTime();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        request.setOrgReqDate(successTime.format(formatter));

        // 2. 设置扩展参数
        Map<String, Object> extendInfoMap = new HashMap<>();
        // 原交易全局流水号（汇付返回的 org_hf_seq_id）
        extendInfoMap.put("org_hf_seq_id", reqDTO.getOrgHfSeqId());
        request.setExtendInfo(extendInfoMap);

        // 3. 发起 API 调用
        Map<String, Object> response = doExecute(request);
        log.info("[汇付-支付宝 Wap 网站支付退款][请求单号:{}] [返回结果:{}]", reqDTO.getOutRefundNo(), response);

        // 4. 校验是否调用成功
//        if (!isSuccess(response)) { // 交易中为 00000100
//            return PayRefundRespDTO.failureOf(Convert.toStr(response.get("resp_code")),
//                    Convert.toStr(response.get("resp_desc")), reqDTO.getOutRefundNo(), response);
//        }
        // 5. 解析返回结果
        String transStat = Convert.toStr(response.get("trans_stat")); // 交易状态：P=处理中，S=成功，F=失败
        String transFinishTime = Convert.toStr(response.get("trans_finish_time")); // 完成时间 yyyyMMddHHmmss
        String hfSeqId = Convert.toStr(response.get("hf_seq_id")); // 汇付全局流水号
        String reqSeqId = Convert.toStr(response.get("req_seq_id")); // 请求流水号(退款单号)

        if ("S".equals(transStat)) { // 成功
            return PayRefundRespDTO.successOf(hfSeqId, parseDate(transFinishTime), reqSeqId, response);
        } else if ("P".equals(transStat)) { // 处理中
            return PayRefundRespDTO.waitingOf(hfSeqId, reqSeqId, response);
        } else { // F 或未知状态，认为失败
            return PayRefundRespDTO.failureOf(Convert.toStr(response.get("resp_code")),
                    Convert.toStr(response.get("resp_desc")), reqDTO.getOutRefundNo(), response);
        }
    }

    @Override
    protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable {
        log.info("[汇付-支付宝 Wap 网站支付退款异步回调参数][返回结果:{}]", params);
        // ========== 1. 提取关键参数 ==========
        String respCode = params.get("resp_code");       // 网关响应码
        String respDesc = params.get("resp_desc");       // 响应描述
        String reqSeqId = params.get("req_seq_id");      // 请求流水号（退款单号）
        String hfSeqId = params.get("hf_seq_id");        // 汇付全局流水号
        String transStat = params.get("trans_stat");     // 交易状态：P=处理中，S=成功，F=失败
        String transFinishTime = params.get("trans_finish_time"); // 完成时间 yyyyMMddHHmmss
        // ========== 2. 校验网关返回码 ==========
        // 汇付规定：00000000 代表接口调用成功，其他代表失败
        if (!"00000000".equals(respCode)) {
            return PayRefundRespDTO.failureOf(respCode, respDesc, reqSeqId, params);
        }
        // ========== 3. 根据交易状态返回业务结果 ==========
        if ("S".equals(transStat)) { // 成功：返回成功的退款响应对象
            return PayRefundRespDTO.successOf(hfSeqId, parseDate(transFinishTime), reqSeqId, params);
        } else if ("P".equals(transStat)) {  // 处理中：返回等待中的退款响应对象
            return PayRefundRespDTO.waitingOf(hfSeqId, reqSeqId, params);
        } else { // F 或未知状态，认为失败
            return PayRefundRespDTO.failureOf(respCode, respDesc, reqSeqId, params);
        }
    }
    /**
     * 查询退款订单状态（调用汇付接口）
     */
    @Override
    protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo, LocalDateTime reqTime) throws Throwable {
        // ================== 1. 组装查询请求参数 ==================
        V2TradePaymentScanpayRefundqueryRequest request = new V2TradePaymentScanpayRefundqueryRequest();
        request.setHuifuId(config.getHuifuId());  // 商户号
        // 退款请求日期，格式：yyyyMMdd
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        request.setOrgReqDate(reqTime.format(formatter));
        // 退款全局流水号 退款请求流水号,退款全局流水号,终端订单号三选一不能都为空
//        request.setOrgHfSeqId("003100TOP2B221110093241P139ac139c0c00000");
        // 退款请求流水号
        request.setOrgReqSeqId(outRefundNo);

        // ================== 2. 发起 API 调用 ==================
        Map<String, Object> response = doExecute(request);
        log.info("[汇付-支付宝 Wap 网站支付退款订单查询][退款单号:{}][返回结果:{}]", outRefundNo, response);

        // ================== 3. 校验接口调用结果 ==================
        if (!isSuccess(response)) {
            return PayRefundRespDTO.failureOf(Convert.toStr(response.get("resp_code")),
                    Convert.toStr(response.get("resp_desc")), outRefundNo, response);
        }

        // ================== 4. 提取关键返回参数 ==================
        String respCode = Convert.toStr(response.get("resp_code"));       // 网关响应码
        String respDesc = Convert.toStr(response.get("resp_desc"));       // 响应描述
        String reqSeqId = Convert.toStr(response.get("org_req_seq_id"));      // 请求流水号（退款单号）
        String hfSeqId = Convert.toStr(response.get("org_hf_seq_id"));        // 汇付全局流水号
        String transStat = Convert.toStr(response.get("trans_stat"));     // 交易状态：P=处理中，S=成功，F=失败
        String transFinishTime = Convert.toStr(response.get("trans_finish_time")); // 完成时间 yyyyMMddHHmmss

        // ================== 5. 根据交易状态返回结果 ==================
        if ("S".equals(transStat)) { // 成功：返回成功的退款响应对象
            return PayRefundRespDTO.successOf(hfSeqId, parseDate(transFinishTime), reqSeqId, response);
        } else if ("P".equals(transStat)) {  // 处理中：返回等待中的退款响应对象
            return PayRefundRespDTO.waitingOf(hfSeqId, reqSeqId, response);
        } else { // F 或未知状态，认为失败
            return PayRefundRespDTO.failureOf(respCode, respDesc, reqSeqId, response);
        }
    }

    @Override
    protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable {
        throw new UnsupportedOperationException("支付渠道的统一【转账】回调 未实现");
    }

    @Override
    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws Throwable {
        throw new UnsupportedOperationException("调用三方渠道发起转账 待实现");
    }

    @Override
    protected PayTransferRespDTO doGetTransfer(String outTradeNo, LocalDateTime createTime, String huifuId, PayTransferTypeEnum type) throws Throwable {
        throw new UnsupportedOperationException("获得转账订单信息 待实现");
    }

    @Override
    protected UserBasicdataRespDTO doCreateUserBasicdataIndv(UserInfoReqDTO userInfoReqDTO) throws Throwable {
        throw new UnsupportedOperationException("创建用户信息 待实现");
    }

    @Override
    protected UserBusiOpenRespDTO doUserBusiOpen(UserInfoReqDTO userInfoReqDTO) throws Throwable {
        throw new UnsupportedOperationException("用户业务入驻 待实现");
    }

    @Override
    protected UserBusiOpenRespDTO doUpdateuserBusiOpen(UserInfoReqDTO userInfoReqDTO) throws Throwable {
        throw new UnsupportedOperationException("待实现");
    }

    /***
     * @Description: 执行API调用
     * @param request
     * @throws Exception  Map<String,Object>
     * @date: 2021年7月5日17:59:37
     */
    public static Map<String, Object> doExecute(BaseRequest request) throws Exception {
        return BasePayClient.request(request, false);
    }

    static String formatDateToYYYYMMDDHHMMSS(LocalDateTime time) {
        return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
    }

    static String formatDateToYYYYMMDD(LocalDateTime time) {
        return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATE_PATTERN);
    }

    private static String convertPriceToYuan(Integer price) {
        return BigDecimal.valueOf(price)
                .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)
                .toPlainString();
    }
    /**
     * 判断接口返回是否成功（根据 resp_code）
     */
    private boolean isSuccess(Map<String, Object> response) {
        return "00000000".equals(Convert.toStr(response.get("resp_code")));
    }
}
